"""
五子棋游戏服务模块

实现五子棋游戏的业务逻辑，连接数据库操作和游戏核心规则。
提供创建游戏、加入游戏、走棋等高级功能。
"""

import json
import random
from typing import Dict, List, Optional, Tuple, Any
from datetime import datetime

from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.modules.gomoku.db.models.game import Game, GameMove, GameStatus, PlayerColor
from app.modules.gomoku.schemas.game import GameMoveCreate, GameUpdate
from app.modules.gomoku.services.game_logic import gomoku_logic


class GameService:
    """
    五子棋游戏服务类
    
    提供游戏相关的业务逻辑功能。
    """
    
    async def create_game(
        self, db: AsyncSession, *, player_id: int, board_size: int = 15
    ) -> Game:
        """
        创建新游戏
        
        Args:
            db: 数据库会话
            player_id: 创建者ID
            board_size: 棋盘大小
            
        Returns:
            创建的游戏对象
        """
        # 创建空棋盘
        board = gomoku_logic.create_empty_board()
        board_state = {
            "size": board_size,
            "board": board,
            "last_move": None
        }
        
        # 创建新游戏
        game = Game(
            player1_id=player_id,
            board_size=board_size,
            board_state=json.dumps(board_state),
            status=GameStatus.WAITING,
            is_draw=False
        )
        
        db.add(game)
        await db.commit()
        await db.refresh(game)
        
        return game
    
    async def join_game(
        self, db: AsyncSession, *, game_id: int, player_id: int
    ) -> Optional[Game]:
        """
        加入游戏
        
        Args:
            db: 数据库会话
            game_id: 游戏ID
            player_id: 加入者ID
            
        Returns:
            更新后的游戏对象，如果操作失败则返回None
        """
        # 获取游戏
        stmt = select(Game).where(Game.id == game_id)
        result = await db.execute(stmt)
        game = result.scalar_one_or_none()
        
        if not game or game.status != GameStatus.WAITING:
            return None
        
        # 玩家不能加入自己创建的游戏
        if game.player1_id == player_id:
            return None
        
        # 更新游戏状态
        game.player2_id = player_id
        game.status = GameStatus.ONGOING
        game.started_at = datetime.utcnow()
        
        # 随机分配颜色
        colors = list(PlayerColor)
        random.shuffle(colors)
        game.player1_color = colors[0]
        game.player2_color = colors[1]
        
        # 黑棋先行
        game.current_turn = PlayerColor.BLACK
        
        await db.commit()
        await db.refresh(game)
        
        return game
    
    async def get_game(
        self, db: AsyncSession, *, game_id: int
    ) -> Optional[Game]:
        """
        获取游戏信息
        
        Args:
            db: 数据库会话
            game_id: 游戏ID
            
        Returns:
            游戏对象，如果不存在则返回None
        """
        stmt = (
            select(Game)
            .options(
                selectinload(Game.player1),
                selectinload(Game.player2),
                selectinload(Game.moves)
            )
            .where(Game.id == game_id)
        )
        result = await db.execute(stmt)
        return result.scalar_one_or_none()
    
    async def make_move(
        self, db: AsyncSession, *, game_id: int, user_id: int, move: GameMoveCreate
    ) -> Tuple[Optional[Game], Optional[GameMove], bool]:
        """
        执行一步棋
        
        Args:
            db: 数据库会话
            game_id: 游戏ID
            user_id: 用户ID
            move: 棋步数据
            
        Returns:
            元组 (更新后的游戏, 创建的棋步, 是否获胜)
            如果操作失败则返回(None, None, False)
        """
        # 获取游戏
        game = await self.get_game(db, game_id=game_id)
        
        # 验证游戏状态
        if not game or game.status != GameStatus.ONGOING:
            return None, None, False
        
        # 验证是否是当前玩家的回合
        player_color = None
        if game.player1_id == user_id:
            player_color = game.player1_color
        elif game.player2_id == user_id:
            player_color = game.player2_color
        else:
            return None, None, False
        
        if player_color != game.current_turn:
            return None, None, False
        
        # 解析棋盘状态
        board_state = gomoku_logic.parse_board_state(game.board_state)
        board = board_state["board"]
        
        # 验证落子是否有效
        if not gomoku_logic.is_valid_move(board, move.x, move.y):
            return None, None, False
        
        # 获取当前移动编号
        move_number = len(game.moves) + 1
        
        # 创建新的棋步记录
        game_move = GameMove(
            game_id=game_id,
            user_id=user_id,
            x=move.x,
            y=move.y,
            color=player_color,
            move_number=move_number
        )
        
        db.add(game_move)
        
        # 更新棋盘状态
        board = gomoku_logic.make_move(board, move.x, move.y, player_color.value)
        board_state["board"] = board
        board_state["last_move"] = {
            "x": move.x,
            "y": move.y,
            "color": player_color.value,
            "player_id": user_id
        }
        game.board_state = gomoku_logic.serialize_board_state(board_state)
        
        # 检查是否获胜
        is_win = gomoku_logic.check_win(board, move.x, move.y, player_color.value)
        
        if is_win:
            # 更新游戏状态为完成
            game.status = GameStatus.COMPLETED
            game.winner_id = user_id
            game.ended_at = datetime.utcnow()
        elif gomoku_logic.is_board_full(board):
            # 如果棋盘已满但没有获胜者，则为平局
            game.status = GameStatus.COMPLETED
            game.is_draw = True
            game.ended_at = datetime.utcnow()
        else:
            # 切换回合
            game.current_turn = (
                PlayerColor.WHITE 
                if player_color == PlayerColor.BLACK 
                else PlayerColor.BLACK
            )
        
        await db.commit()
        await db.refresh(game)
        await db.refresh(game_move)
        
        return game, game_move, is_win
    
    async def get_user_games(
        self, db: AsyncSession, *, user_id: int, skip: int = 0, limit: int = 10
    ) -> List[Game]:
        """
        获取用户参与的游戏列表
        
        Args:
            db: 数据库会话
            user_id: 用户ID
            skip: 跳过记录数
            limit: 返回记录数
            
        Returns:
            游戏列表
        """
        stmt = (
            select(Game)
            .options(
                selectinload(Game.player1),
                selectinload(Game.player2)
            )
            .where((Game.player1_id == user_id) | (Game.player2_id == user_id))
            .order_by(Game.created_at.desc())
            .offset(skip)
            .limit(limit)
        )
        result = await db.execute(stmt)
        return list(result.scalars().all())
    
    async def get_waiting_games(
        self, db: AsyncSession, *, skip: int = 0, limit: int = 10
    ) -> List[Game]:
        """
        获取等待加入的游戏列表
        
        Args:
            db: 数据库会话
            skip: 跳过记录数
            limit: 返回记录数
            
        Returns:
            游戏列表
        """
        stmt = (
            select(Game)
            .options(selectinload(Game.player1))
            .where(Game.status == GameStatus.WAITING)
            .order_by(Game.created_at.desc())
            .offset(skip)
            .limit(limit)
        )
        result = await db.execute(stmt)
        return list(result.scalars().all())
    
    async def resign_game(
        self, db: AsyncSession, *, game_id: int, user_id: int
    ) -> Optional[Game]:
        """
        认输
        
        Args:
            db: 数据库会话
            game_id: 游戏ID
            user_id: 认输玩家ID
            
        Returns:
            更新后的游戏对象，如果操作失败则返回None
        """
        # 获取游戏
        game = await self.get_game(db, game_id=game_id)
        
        # 验证游戏状态
        if not game or game.status != GameStatus.ONGOING:
            return None
        
        # 验证用户是否是游戏参与者
        if game.player1_id != user_id and game.player2_id != user_id:
            return None
        
        # 设置胜者为对手
        winner_id = game.player2_id if user_id == game.player1_id else game.player1_id
        
        # 更新游戏状态
        game.status = GameStatus.COMPLETED
        game.winner_id = winner_id
        game.ended_at = datetime.utcnow()
        
        await db.commit()
        await db.refresh(game)
        
        return game 